from flask import Blueprint, request, jsonify,render_template
import json
from apps.extensions import db
from apps.common.utils.rights import authorize
from apps.common.utils.validate import str_escape
from apps.common.utils.http import table_api, success_api, fail_api
from apps.modules.admin.models.cms.cms_post import Post
from apps.modules.admin.models.cms.cms_tag import Tag
from apps.modules.admin.models.cms.cms_category import Category
from apps.common.curd import get_one_by_id
from apps.common.utils import upload as upload_curd
from apps.modules.admin.schemas.cms.cms_post import PostSchema
import datetime
from apps.common.utils.markdownify_converter import (
    html_to_markdown_markdownify,
    markdown_to_html_markdownify
)

post_bp = Blueprint('post', __name__, url_prefix='/post')

def update_category_count(category_id):
    """
    更新分类的使用次数
    """
    if not category_id:
        return
    
    # 统计该分类下的文章数量（包括子分类）
    def count_posts_in_category(cat_id):
        # 直接属于该分类的文章
        direct_count = Post.query.filter_by(category_id=cat_id, deleted=False).count()
        
        # 递归统计子分类的文章
        child_categories = Category.query.filter_by(parent_id=cat_id, deleted=False).all()
        child_count = sum(count_posts_in_category(child.id) for child in child_categories)
        
        return direct_count + child_count
    
    # 更新分类的count字段
    category = Category.query.get(category_id)
    if category:
        category.count = count_posts_in_category(category_id)
        db.session.add(category)

def update_tag_count(tag_id):
    """
    更新标签的使用次数
    """
    if not tag_id:
        return
    
    from apps.modules.admin.models.cms.cms_post_tag import post_tag
    
    # 统计该标签关联的文章数量
    post_count = db.session.query(post_tag).filter(
        post_tag.c.tag_id == tag_id
    ).join(Post, post_tag.c.post_id == Post.id).filter(
        Post.deleted == False
    ).count()
    
    # 更新标签的count字段
    tag = Tag.query.get(tag_id)
    if tag:
        tag.count = post_count
        db.session.add(tag)

def update_all_counts(post_id=None, old_category_id=None, old_tag_ids=None, new_category_id=None, new_tag_ids=None):
    """
    批量更新分类和标签的使用次数
    """
    # 需要更新的分类ID集合
    category_ids_to_update = set()
    if old_category_id:
        category_ids_to_update.add(old_category_id)
    if new_category_id:
        category_ids_to_update.add(new_category_id)
    
    # 需要更新的标签ID集合
    tag_ids_to_update = set()
    if old_tag_ids:
        tag_ids_to_update.update(old_tag_ids)
    if new_tag_ids:
        tag_ids_to_update.update(new_tag_ids)
    
    # 更新分类count
    for category_id in category_ids_to_update:
        update_category_count(category_id)
    
    # 更新标签count
    for tag_id in tag_ids_to_update:
        update_tag_count(tag_id)

@post_bp.get('/')
@authorize("cms:post:main")
def main():
    """
    文章管理
    """
    return render_template('cms/post/main.html')


@post_bp.get('/data')
@authorize("cms:post:main")
def table():
    """
    文章列表
    """

    filters = []
    post_type = 'post'
    if post_type:
        filters.append(Post.post_type.contains(post_type))

    title = str_escape(request.args.get('title', type=str))
    if title:
        filters.append(Post.title.contains(title))
    
    # 使用joinedload预加载分类和标签数据，避免N+1查询问题
    posts = Post.query.options(
        db.joinedload(Post.category),
        db.joinedload(Post.tags)
    ).filter(*filters).layui_paginate()
    
    return table_api(data=PostSchema(many=True).dump(posts), count=posts.total)



@post_bp.get('/add')
@authorize("cms:post:add", log=True)
def add():
    """
    文章增加
    """
    return render_template('cms/post/add.html')


@post_bp.get('/add_wang_editor')
@authorize("cms:post:add", log=True)
def add_wang_editor():
    """
    wang_editor文章增加
    """
    return render_template('cms/post/add_wangeditor.html')

@post_bp.get('/add_markdown')
@authorize("cms:post:add", log=True)
def add_markdown():
    """
    Markdown文章增加
    """
    return render_template('cms/post/add_markdown.html')



@post_bp.get('/edit/<int:id>')
@authorize("cms:post:edit", log=True)
def edit(id):
    """
    文章修改
    """
    post = get_one_by_id(model=Post, id=id)
    if not post:
        return fail_api(msg="文章不存在")
    
    # 获取文章的标签
    tags = []
    for tag in post.tags:
        tags.append({
            'id': tag.id,
            'title': tag.title,
            'alias': tag.alias
        })
    
    # 将SQLAlchemy对象转换为字典，以便在模板中使用
    post_dict = {
        'id': post.id,
        'title': post.title,
        'slug': post.slug,
        'content': post.content,
        'excerpt': post.excerpt,
        'status': post.status,
        'author_id': post.author_id,
        'category_id': post.category_id,
        'featured_image': post.featured_image,
        'is_featured': post.is_featured,
        'is_sticky': post.is_sticky,
        'allow_comments': post.allow_comments,
        'meta_title': post.meta_title,
        'meta_description': post.meta_description,
        'meta_keywords': post.meta_keywords,
        'published_at': post.published_at.isoformat() if post.published_at else None,
        'created_at': post.created_at.isoformat() if post.created_at else None,
        'updated_at': post.updated_at.isoformat() if post.updated_at else None,
        'tags': tags
    }
    
    # 将文章内容转换为JSON字符串，以便在JavaScript中使用
    import json
    post_content_json = json.dumps(post.content) if post.content else '""'
    
    return render_template('cms/post/edit.html', post=post_dict)


@post_bp.get('/edit_markdown/<int:id>')
@authorize("cms:post:edit", log=True)
def edit_markdown(id):
    """
    Markdown文章修改
    """
    post = get_one_by_id(model=Post, id=id)
    if not post:
        return fail_api(msg="文章不存在")
    
    # 获取文章的标签
    tags = []
    for tag in post.tags:
        tags.append({
            'id': tag.id,
            'title': tag.title,
            'alias': tag.alias
        })
    content = html_to_markdown_markdownify(post.content)
    # 将SQLAlchemy对象转换为字典，以便在模板中使用
    post_dict = {
        'id': post.id,
        'title': post.title,
        'slug': post.slug,
        'content': content,
        'excerpt': post.excerpt,
        'type': post.type,
        'status': post.status,
        'author_id': post.author_id,
        'category_id': post.category_id,
        'featured_image': post.featured_image,
        'is_featured': post.is_featured,
        'is_sticky': post.is_sticky,
        'allow_comments': post.allow_comments,
        'meta_title': post.meta_title,
        'meta_description': post.meta_description,
        'meta_keywords': post.meta_keywords,
        'published_at': post.published_at.isoformat() if post.published_at else None,
        'created_at': post.created_at.isoformat() if post.created_at else None,
        'updated_at': post.updated_at.isoformat() if post.updated_at else None,
        'tags': tags
    }
    
    return render_template('cms/post/edit_markdown.html', post=post_dict)


@post_bp.get('/edit_wang_editor/<int:id>')
@authorize("cms:post:edit", log=True)
def edit_wang_editor(id):
    """
    wang_editor文章修改
    """
    post = get_one_by_id(model=Post, id=id)
    if not post:
        return fail_api(msg="文章不存在")

    # 获取文章的标签
    tags = []
    for tag in post.tags:
        tags.append({
            'id': tag.id,
            'title': tag.title,
            'alias': tag.alias
        })

    # 将SQLAlchemy对象转换为字典，以便在模板中使用
    post_dict = {
        'id': post.id,
        'title': post.title,
        'slug': post.slug,
        'content': post.content,
        'excerpt': post.excerpt,
        'type': post.type,
        'status': post.status,
        'author_id': post.author_id,
        'category_id': post.category_id,
        'featured_image': post.featured_image,
        'is_featured': post.is_featured,
        'is_sticky': post.is_sticky,
        'allow_comments': post.allow_comments,
        'meta_title': post.meta_title,
        'meta_description': post.meta_description,
        'meta_keywords': post.meta_keywords,
        'published_at': post.published_at.isoformat() if post.published_at else None,
        'created_at': post.created_at.isoformat() if post.created_at else None,
        'updated_at': post.updated_at.isoformat() if post.updated_at else None,
        'tags': tags
    }

    return render_template('cms/post/edit_wangeditor.html', post=post_dict)


@post_bp.get('/content/<int:id>')
@authorize("cms:post:edit", log=True)
def get_content(id):
    """
    获取文章内容
    """
    post = get_one_by_id(model=Post, id=id)
    if not post:
        return fail_api(msg="文章不存在")
    
    return success_api(msg="获取成功", data={"content": post.content})


@post_bp.get('/categories')
@authorize("cms:post:edit", log=True)
def get_categories():
    """
    获取分类列表（用于选择）- 树形结构
    """
    # Category已经在文件顶部导入
    
    # 获取搜索关键词
    search = request.args.get('search', '').strip()
    
    # 构建查询
    query = Category.query.filter_by(deleted=False)
    if search:
        query = query.filter(Category.title.contains(search))
    
    # 获取所有分类
    categories = query.all()
    
    # 转换为字典格式
    flat_data = []
    for category in categories:
        flat_data.append({
            'id': category.id,
            'title': category.title,
            'alias': category.alias,
            'parent_id': category.parent_id or 0
        })
    
    # 构建树形结构
    def build_tree(items, parent_id=0):
        tree = []
        for item in items:
            if item['parent_id'] == parent_id:
                children = build_tree(items, item['id'])
                if children:
                    item['children'] = children
                tree.append(item)
        return tree
    
    tree_data = build_tree(flat_data)
    
    return success_api(msg="获取成功", data=tree_data)


@post_bp.get('/tags')
@authorize("cms:post:edit", log=True)
def get_tags():
    """
    获取标签列表（用于选择）
    """
    # Tag已经在文件顶部导入
    
    # 获取搜索关键词
    search = request.args.get('search', '').strip()
    
    # 构建查询
    query = Tag.query.filter_by(deleted=False)
    if search:
        query = query.filter(Tag.title.contains(search))
    
    # 获取所有标签
    tags = query.all()
    
    # 转换为字典格式
    data = []
    for tag in tags:
        data.append({
            'id': tag.id,
            'title': tag.title,
            'alias': tag.alias
        })
    
    return success_api(msg="获取成功", data=data)


@post_bp.get('/post-tags/<int:post_id>')
@authorize("cms:post:edit", log=True)
def get_post_tags(post_id):
    """
    获取文章的标签列表
    """
    post = get_one_by_id(model=Post, id=post_id)
    if not post:
        return fail_api(msg="文章不存在")
    
    # 获取文章的标签
    tags = []
    for tag in post.tags:
        tags.append({
            'id': tag.id,
            'title': tag.title,
            'alias': tag.alias
        })
    
    return success_api(msg="获取成功", data=tags)

@post_bp.delete('/remove/<int:id>')
@authorize("cms:post:remove", log=True)
def remove(id):
    """
    文章删除
    """
    post = Post.query.filter_by(id=id).first()

    if not post:
        return fail_api(msg="文章不存在")
    
    # 获取文章的分类和标签信息（用于更新count）
    old_category_id = post.category_id
    old_tag_ids = [tag.id for tag in post.tags]
    
    # 清除文章的标签关联关系
    post.tags.clear()
    
    # 删除文章
    p = Post.query.filter_by(id=id).delete()
    
    # 更新分类和标签的使用次数
    update_all_counts(
        old_category_id=old_category_id,
        old_tag_ids=old_tag_ids
    )
    
    db.session.commit()
    if not p:
        return fail_api(msg="文章删除失败")
    return success_api(msg="文章删除成功")


@post_bp.delete('/batchRemove')
@authorize("cms:post:remove", log=True)
def batch_remove():
    """
    批量删除
    """
    ids = request.form.getlist('ids[]')
    
    # 收集所有需要更新的分类和标签ID
    category_ids_to_update = set()
    tag_ids_to_update = set()
    
    for id in ids:
        post = Post.query.filter_by(id=id).first()
        if post:
            # 收集分类ID
            if post.category_id:
                category_ids_to_update.add(post.category_id)
            
            # 收集标签ID
            for tag in post.tags:
                tag_ids_to_update.add(tag.id)
    
    # 删除文章
    for id in ids:
        post = Post.query.filter_by(id=id).first()
        if post:
            # 清除文章的标签关联关系
            post.tags.clear()
            # 删除文章
            res = Post.query.filter_by(id=id).delete()
            if not res:
                return fail_api(msg="批量删除失败")
        else:
            return fail_api(msg=f"文章ID {id} 不存在")
    
    # 更新分类和标签的使用次数
    for category_id in category_ids_to_update:
        update_category_count(category_id)
    
    for tag_id in tag_ids_to_update:
        update_tag_count(tag_id)
    
    db.session.commit()
    return success_api(msg="批量删除成功")


@post_bp.post('/upload')
@authorize("cms:post:edit", log=True)
def upload_api():
    """
    上传文件 - 支持图片和视频
    """
    # 检查是否有文件上传
    if not request.files:
        return fail_api(msg="没有选择文件")
    
    # 根据uploadFormName获取文件
    file = None
    file_type = None
    
    if 'image' in request.files:
        file = request.files['image']
        file_type = 'images'
    elif 'video' in request.files:
        file = request.files['video']
        file_type = 'video'
    elif 'attachment' in request.files:
        file = request.files['attachment']
        file_type = 'attachment'
    else:
        return fail_api(msg="未找到有效的文件")
    
    # 检查是否有meta参数指定文件类型
    if request.form.get('meta'):
        try:
            meta_data = json.loads(request.form.get('meta'))
            if meta_data.get('type'):
                file_type = meta_data['type']
        except:
            pass  # 如果meta解析失败，使用默认的文件类型检测
    
    if not file or not file.filename:
        return fail_api(msg="文件无效")
    
    mime = file.content_type
    
    try:
        # 检查文件是否允许上传
        if not upload_curd.is_allowed_file(file.filename, file_type):
            return fail_api(msg=f"不支持的文件类型: {file.filename}")
        
        # 上传文件
        result = upload_curd.upload_file_by_type(file=file, mime=mime, file_type=file_type)
        
        # 根据文件类型返回不同的响应格式
        if file_type == 'images':
            # 图片响应格式
            res = {
                "errorCode": 0,
                "data": {
                    "src": "/admin" + result['url'],
                    "alt": result['filename']
                }
            }
        elif file_type == 'video':
            # 视频响应格式
            res = {
                "errorCode": 0,
                "data": {
                    "src": "/admin" + result['url'],
                    "poster": "",  # 视频封面，可选
                    "alt": result['filename']
                }
            }
        elif file_type == 'attachment':
            # 附件响应格式
            res = {
                "errorCode": 0,
                "data": {
                    "href": "/admin" + result['url'],
                    "fileName": result['filename']
                }
            }
        return jsonify(res)
        
    except Exception as e:
        return fail_api(msg=f"上传失败: {str(e)}")


@post_bp.post('/save')
@authorize("cms:post:add", log=True)
def save():
    """
    保存文章
    """
    try:
        data = request.get_json()
        
        # 验证必填字段
        if not data.get('title'):
            return fail_api(msg="文章标题不能为空")
        if not data.get('slug'):
            return fail_api(msg="文章别名不能为空")
        
        # 检查别名是否已存在
        existing_post = Post.query.filter_by(slug=data['slug']).first()
        if existing_post:
            return fail_api(msg="文章别名已存在，请使用其他别名")
        
        # 获取当前用户ID（假设从session或token中获取）
        from flask_login import current_user
        author_id = current_user.id if current_user.is_authenticated else 1  # 默认作者ID为1
        type = data.get('type', 'wangEditor')
        content = ''
        if type == 'markdown':
            content = markdown_to_html_markdownify(data.get('content', ''))
        if type == 'wangEditor':
            content = data.get('content', '')
        # 创建新文章
        new_post = Post(
            title=data.get('title'),
            slug=data.get('slug'),
            content=content,
            excerpt=data.get('excerpt', ''),
            type=data.get('type', 'wangEditor'),
            post_type=data.get('post_type', 'post'),
            featured_image=data.get('featured_image', ''),
            status=data.get('status', 'draft'),
            author_id=author_id,
            category_id=data.get('category_id'),
            is_featured=data.get('is_featured', False),
            is_sticky=data.get('is_sticky', False),
            allow_comments=data.get('allow_comments', True),
            meta_title=data.get('meta_title', ''),
            meta_description=data.get('meta_description', ''),
            meta_keywords=data.get('meta_keywords', ''),
            published_at=datetime.datetime.now() if data.get('status') == 'published' else None
        )
        
        # 保存文章到数据库
        db.session.add(new_post)
        db.session.flush()  # 获取文章ID
        
        # 处理标签关联
        tag_ids = data.get('tag_ids', [])
        if tag_ids:
            tags = Tag.query.filter(Tag.id.in_(tag_ids)).all()
            new_post.tags = tags
        
        # 更新分类和标签的使用次数
        update_all_counts(
            new_category_id=new_post.category_id,
            new_tag_ids=tag_ids
        )
        
        db.session.commit()
        
        return success_api(msg="文章保存成功", data={"id": new_post.id})
        
    except Exception as e:
        db.session.rollback()
        return fail_api(msg=f"保存失败: {str(e)}")


@post_bp.post('/update/<int:id>')
@authorize("cms:post:edit", log=True)
def update(id):
    """
    更新文章
    """
    try:
        post = Post.query.filter_by(id=id).first()
        if not post:
            return fail_api(msg="文章不存在")
        
        data = request.get_json()
        
        # 验证必填字段
        if not data.get('title'):
            return fail_api(msg="文章标题不能为空")
        if not data.get('slug'):
            return fail_api(msg="文章别名不能为空")
        
        # 检查别名是否已被其他文章使用
        existing_post = Post.query.filter_by(slug=data['slug']).first()
        if existing_post and existing_post.id != id:
            return fail_api(msg="文章别名已存在，请使用其他别名")
        type = data.get('type', 'wangEditor')
        content = ''
        if type == 'markdown':
            content = markdown_to_html_markdownify(data.get('content', ''))
        if type == 'wangEditor':
            content = data.get('content', '')
        # 更新文章信息
        post.title = data.get('title')
        post.slug = data.get('slug')
        post.content = content
        post.excerpt = data.get('excerpt', '')
        post.type = data.get('type', 'wangEditor')
        post.post_type = data.get('post_type', 'post')
        post.featured_image = data.get('featured_image', '')
        post.status = data.get('status', 'draft')
        post.category_id = data.get('category_id')
        post.is_featured = data.get('is_featured', False)
        post.is_sticky = data.get('is_sticky', False)
        post.allow_comments = data.get('allow_comments', True)
        post.meta_title = data.get('meta_title', '')
        post.meta_description = data.get('meta_description', '')
        post.meta_keywords = data.get('meta_keywords', '')
        
        # 如果状态改为已发布且之前未发布，设置发布时间
        if data.get('status') == 'published' and not post.published_at:
            post.published_at = datetime.datetime.now()
        
        # 获取旧的分类和标签信息（用于更新count）
        old_category_id = post.category_id
        old_tag_ids = [tag.id for tag in post.tags]
        
        # 处理标签关联
        tag_ids = data.get('tag_ids', [])
        # Tag已经在文件顶部导入
        if tag_ids:
            tags = Tag.query.filter(Tag.id.in_(tag_ids)).all()
            post.tags = tags
        else:
            post.tags = []
        
        # 更新分类和标签的使用次数
        update_all_counts(
            old_category_id=old_category_id,
            old_tag_ids=old_tag_ids,
            new_category_id=post.category_id,
            new_tag_ids=tag_ids
        )
        
        # 保存到数据库
        db.session.commit()
        
        return success_api(msg="文章更新成功")
        
    except Exception as e:
        db.session.rollback()
        return fail_api(msg=f"更新失败: {str(e)}")